﻿#include "stdafx.h"
#ifdef LK_WINDOWS_WAS_RECORDER_
namespace LK
{
	namespace Windows
	{
		namespace WAS
		{
			using namespace ::LK::Sound;
			//Recorder类
			Recorder::Recorder()
				:hz(DefaultHz), ch(DefaultChannels), bits(DefaultBits), isFloat(false),//设置默认格式
				isRecording(false), started(false), isSelecting(false), isClosing(false), UsingDefaultDevice(true),
				DeviceID(ptr<String>::GetEmpty()), DeviceName(ptr<String>::GetEmpty()),
				pDevice(NULL), pAudioClient(NULL), pCaptureClient(NULL),
				CurrentFormat(),
				hnsRequestedDuration(SoundEnvironment::GetDefaultRef().DefaultRecorderRequestedDuration),
				converter()
			{
				buffer.SetEnableThreadSafe(true);
				SelectDeviceById(ptr<String>::GetEmpty());
				//在environment中注册
				SoundEnvironment::GetDefaultRef().AddRecorder(this);
			}
			Recorder::~Recorder()
			{
				SoundEnvironment::GetDefaultRef().RemoveRecorder(this);//在environment中移除
				tc.Lock();
				Close();
				int i = 0;
				while (tc.GetCoutnt() >= 0 && i++ < 500){ isClosing = 1; Sleep(10); }
			}
			void Recorder::Start()
			{
				while (!tc.Try()){ if (isClosing || isSelecting)return; Sleep(1); }
				if (pAudioClient)
				{
					pAudioClient->Start();
					started = true;
					isRecording = true;
				}
				tc.UnLock();
			}
			void Recorder::Stop()
			{
				while (!tc.Try()){ if (isClosing || isSelecting)return; Sleep(1); }
				if (isRecording && pAudioClient)
				{
					isRecording = false;
					started = false;
					pAudioClient->Stop();
				}
				tc.UnLock();
			}
			void Recorder::Pause()
			{
				while (!tc.Try()){ if (isClosing || isSelecting)return; Sleep(1); }
				if (isRecording && pAudioClient)
				{
					isRecording = false;
					pAudioClient->Stop();
				}
				tc.UnLock();
			}
			void Recorder::Close()
			{
				tc.Lock();
				isClosing = true;
				Stop();
				const_cast<ptr<String>&>(DeviceName) = ptr<String>::GetEmpty();
				const_cast<ptr<String>&>(DeviceID) = ptr<String>::GetEmpty();
				if (pCaptureClient)pCaptureClient->Release();
				pCaptureClient = NULL;
				if (pAudioClient)pAudioClient->Release();
				pAudioClient = NULL;
				if (pDevice)pDevice->Release();
				pDevice = NULL;
				isClosing = false;
				tc.UnLock();
			}
			void Recorder::Clear()
			{
				while (!tc.Try()){ if (isClosing)return; Sleep(1); }
				buffer.Reset();
				tc.UnLock();
			}
			double Recorder::GetHz()const
			{
				return hz;
			}
			short Recorder::GetChannels()const
			{
				return ch;
			}
			short Recorder::GetBits()const
			{
				return bits;
			}
			bool Recorder::GetIsFloat()const
			{
				return isFloat;
			}
			void Recorder::SetOutFormat(double h, short bit, short channel, bool isfloat)
			{
				if (h <= 0 || this->hz == h && this->ch == channel && this->bits == bit && this->isFloat == isfloat)return;
				while (!tc.Try()){ if (isClosing || isSelecting)return; Sleep(1); }
				buffer.Reset();
				this->hz = h;
				this->ch = channel;
				this->bits = bit;
				this->isFloat = isfloat;
				tc.UnLock();
				return;
			}
			void Recorder::SetOutFormat(const WAVEFORMATEX&pwfx)
			{
				SetOutFormat(pwfx.nSamplesPerSec, pwfx.wBitsPerSample, pwfx.nChannels, pwfx.wFormatTag == 0xfffe);
			}
			void Recorder::GetOutFormat(int* phz, short* pbits, short* pch, bool* pisfloat)
			{
				if (phz)*phz = (int)hz;
				if (pch)*pch = ch;
				if (pbits)*pbits = bits;
				if (pisfloat)*pisfloat = isFloat;
			}
			void Recorder::GetOutFormat(double* phz, short* pbits, short* pch, bool* pisfloat)
			{
				if (phz)*phz = hz;
				if (pch)*pch = ch;
				if (pbits)*pbits = bits;
				if (pisfloat)*pisfloat = isFloat;
			}
			WaveFormat Recorder::GetOutFormat()
			{
				int h; short bit, channel; bool isfloat;
				GetOutFormat(&h, &bit, &channel, &isfloat);
				return Wave::MakeFormat(h, bit, channel, isfloat);
			}
			WAVEFORMATEX& Recorder::GetSourceFormat()
			{
				return CurrentFormat;
			}
			void Recorder::GetSourceFormat(int* phz, short* pbits, short* pch, bool* pisfloat)
			{
				if (phz)*phz = CurrentFormat.nSamplesPerSec;
				if (pch)*pch = CurrentFormat.nChannels;
				if (pbits)*pbits = CurrentFormat.wBitsPerSample;
				if (pisfloat)*pisfloat = CurrentFormat.wFormatTag == 0xfffe;
			}
			void Recorder::SetVol(double vol)
			{
				converter.Volume = vol;
			}
			double Recorder::GetVol()const
			{
				return converter.Volume;
			}
			void Recorder::SetBufferTime(const ptr<Time>& hus)
			{
				ptr<Time> hms(hus);
				if (hms < SoundEnvironment::GetDefaultRef().MinimumRecorderRequestedDuration)//限制
					hms = SoundEnvironment::GetDefaultRef().MinimumRecorderRequestedDuration;
				if (hms == hnsRequestedDuration)return;
				hnsRequestedDuration = hms;
				bool pa = started && !isRecording;
				SelectDeviceById(DeviceID);
				if (pa)Pause();
			}
			bool Recorder::SelectDeviceById(const ptr<String> id)
			{
				while (!tc.Try()){ if (isClosing)return false; Sleep(1); }
				isSelecting = true;
				HRESULT hr = 0;
				//获取默认设备
				Close();//关闭，重新建立会话
				if (pDevice = id.IsNullOrEmpty() ? SoundEnvironment::GetDefaultRef().GetDefaultRecordingDevice() : SoundEnvironment::GetDefaultRef().GetDevice(id))//获取设备成功
				{
					const_cast<ptr<String>&>(DeviceID) = id;
					const_cast<ptr<String>&>(DeviceName) = SoundEnvironment::GetDefaultRef().GetDeviceName(DeviceID);//设备名称
					if (SUCCEEDED(hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))//激活会话
					{
						//获取混音格式
						WAVEFORMATEX* pwfx;
						if (FAILED(hr = pAudioClient->GetMixFormat(&pwfx)))goto End;
						CurrentFormat = *pwfx;
						//设置音源格式
						converter.SetFormat(CurrentFormat);//采样率和浮点转换
						//使用共享模式，按hnsRequestedDuration的时间间隔进行初始化,Initialize函数很挑剔，只识别CoTaskMemalloc分配的内存
						if (FAILED(hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, (REFERENCE_TIME)((double)hnsRequestedDuration * 10000000), 0, pwfx, NULL)))
						{
							CoTaskMemFree(pwfx);
							goto End;
						}
						CoTaskMemFree(pwfx);
						//获取块帧数
						if (FAILED(hr = pAudioClient->GetBufferSize(&bufferFrameCount)))goto End;
						//获取播放服务
						if (FAILED(hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient)))goto End;
						isSelecting = false;
						tc.UnLock();
						return true;
					}
				}
			End:;
				isSelecting = false;
				if (FAILED(hr))
				{
					Close();
					tc.UnLock();
					Throw<PSoundException >(hr);
				}
				tc.UnLock();
				return false;
			}
			PByteArray Recorder::Read(int len)
			{
				if (!pCaptureClient)return new LK::Collection::DefaultArray<unsigned char>(0);
				while (!tc.Try()){ if (isClosing)return new LK::Collection::DefaultArray<unsigned char>(0); Sleep(1); }
				if (!pCaptureClient)
				{
					tc.UnLock();
					return new LK::Collection::DefaultArray<unsigned char>(0);
				}
				HRESULT hr = 0;
				BYTE *pData;
				UINT32 numFramesAvailable;
				DWORD flags;
				if ((int)buffer.GetLength() < (len * 2))//缓冲区域过小
				{
					buffer.Resize(len * 2);
				}
				if (!started)Start();//自动开始
				while (started && pCaptureClient)
				{
					if ((int)buffer.GetUnreadSize() < len)//缓冲区内容过少
					{
						if (isClosing || isSelecting)
						{
							tc.UnLock();
							return new LK::Collection::DefaultArray<unsigned char>(0);
						}
						if (isRecording)
						{
							if (FAILED(hr = pCaptureClient->GetNextPacketSize(&numFramesAvailable))){ goto End; }//获取可读数据
						}
						else numFramesAvailable = 0;
						if (!numFramesAvailable)
						{
							Sleep(10);
							continue;
						}

						if (SUCCEEDED(hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL)) && !(flags & AUDCLNT_BUFFERFLAGS_SILENT))////获取可读数据;非静音
						{
							if (numFramesAvailable && pData)
							{
								PByteArray tmp1 = converter.Convert<u8>(new LK::Collection::ReferenceArray<unsigned char>(numFramesAvailable*CurrentFormat.nBlockAlign, pData), hz, bits, ch, isFloat);//获得转换后的新内存和写入帧数量
								buffer.Write(tmp1);//写入数据到缓冲区，这要求缓冲区有足够的空间
							}
						}
						else
						{
							Sleep(10);
							break;
						}
						if (FAILED(hr = pCaptureClient->ReleaseBuffer(numFramesAvailable))){ goto End; }
						continue;
					}
					break;
				}
			End:;
				if (FAILED(hr))
				{
					tc.UnLock();
					Throw<PSoundException >(hr);
				}
				PByteArray result = (int)buffer.GetUnreadSize() >= len ? buffer.Read(len) : new LK::Collection::DefaultArray<unsigned char>(0);//读取数据
				tc.UnLock();
				return result;
			}
			PWaveData Recorder::Read()
			{
				if (!pCaptureClient)return PWaveData();
				while (!tc.Try()){ if (isClosing)return PWaveData(); Sleep(1); }
				if (!pCaptureClient)
				{
					tc.UnLock();
					return PWaveData();
				}
				HRESULT hr = 0;
				BYTE *pData;
				UINT32 numFramesAvailable;
				DWORD flags;
				PWaveData tdata(new WaveData());
				if (!started)Start();//自动开始
				while (started && pCaptureClient)
				{
					if (isClosing || isSelecting)
					{
						tc.UnLock();
						return PWaveData();
					}
					if (isRecording)
					{
						if (FAILED(hr = pCaptureClient->GetNextPacketSize(&numFramesAvailable))){ goto End; }//获取可读数据
					}
					else numFramesAvailable = 0;
					if (!numFramesAvailable)
					{
						Sleep(10);
						continue;
					}

					if (SUCCEEDED(hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL)) && !(flags & AUDCLNT_BUFFERFLAGS_SILENT))////获取可读数据;非静音
					{
						if (numFramesAvailable && pData)
						{
							tdata.SetFormat(CurrentFormat);
							tdata.GetData() = new LK::Collection::ReferenceArray<unsigned char>(numFramesAvailable*CurrentFormat.nBlockAlign, pData);
						}
					}
					else
					{
						Sleep(10);
						break;
					}
					hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
					break;
				}
			End:;
				if (FAILED(hr))
				{
					tc.UnLock();
					Throw<PSoundException >(hr);
				}
				tc.UnLock();
				return tdata;
			}
			void Recorder::Restart()
			{
				bool pa = started && !isRecording;
				SelectDeviceById(DeviceID);
				if (pa)Pause();
				else Start();
			}
			//设备添加事件
			void Recorder::OnDeviceAdded()
			{
				try{
					if (SoundEnvironment::GetDefaultRef().OnAddedDeviceId == DeviceID)Restart();
				}
				catch (...)
				{
					Sleep(500);
					try{//再次尝试
						SelectDeviceById(SoundEnvironment::GetDefaultRef().OnAddedDeviceId);
					}
					catch (...){}
				}
			}
			//设备状态改变事件
			void Recorder::OnDeviceStateChanged()
			{}
			//设备移除事件
			void Recorder::OnDeviceRemoved()
			{
				try{
					if (SoundEnvironment::GetDefaultRef().OnRemovedDeviceId == DeviceID)Close();
				}
				catch (...)
				{
				}
			}
			//默认设备改变事件
			void Recorder::OnDefaultDeviceChanged()
			{
				try{
					if (UsingDefaultDevice && SoundEnvironment::GetDefaultRef().OnDefaultChangedFlow == eCapture)SelectDeviceById(SoundEnvironment::GetDefaultRef().OnDefaultChangedDeviceId);
				}
				catch (...)
				{
					Sleep(500);
					try{
						if (UsingDefaultDevice && SoundEnvironment::GetDefaultRef().OnDefaultChangedFlow == eCapture)SelectDeviceById(SoundEnvironment::GetDefaultRef().OnDefaultChangedDeviceId);
					}
					catch (...)
					{
					}
				}
			}
			//设备属性改变事件
			void Recorder::OnDevicePropertyValueChanged()
			{}
		}
	}
}
#endif